/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.integration.config.xml; import java.util.List; import java.util.Map; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.BeanReference; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.ManagedMap; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.core.Conventions; import org.springframework.expression.common.LiteralExpression; import org.springframework.integration.channel.FixedSubscriberChannel; import org.springframework.integration.config.ExpressionFactoryBean; import org.springframework.integration.config.FixedSubscriberChannelBeanFactoryPostProcessor; import org.springframework.integration.config.IntegrationConfigUtils; import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.integration.endpoint.AbstractPollingEndpoint; import org.springframework.integration.transaction.TransactionHandleMessageAdvice; import org.springframework.transaction.interceptor.DefaultTransactionAttribute; import org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource; import org.springframework.transaction.interceptor.TransactionInterceptor; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; /** * Shared utility methods for integration namespace parsers. * * @author Mark Fisher * @author Marius Bogoevici * @author Alex Peters * @author Oleg Zhurakousky * @author Gary Russell * @author Artem Bilan * @author Gunnar Hillert * */ public abstract class IntegrationNamespaceUtils { public static final String REF_ATTRIBUTE = "ref"; public static final String METHOD_ATTRIBUTE = "method"; public static final String ORDER = "order"; public static final String EXPRESSION_ATTRIBUTE = "expression"; public static final String REQUEST_HANDLER_ADVICE_CHAIN = "request-handler-advice-chain"; public static final String AUTO_STARTUP = "auto-startup"; public static final String PHASE = "phase"; public static final String ROLE = "role"; /** * Configures the provided bean definition builder with a property value corresponding to the attribute whose name * is provided if that attribute is defined in the given element. * * @param builder the bean definition builder to be configured * @param element the XML element where the attribute should be defined * @param attributeName the name of the attribute whose value will be used to populate the property * @param propertyName the name of the property to be populated */ public static void setValueIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName, String propertyName) { setValueIfAttributeDefined(builder, element, attributeName, propertyName, false); } /** * Configures the provided bean definition builder with a property value corresponding to the attribute whose name * is provided if that attribute is defined in the given element. * * <p> * The property name will be the camel-case equivalent of the lower case hyphen separated attribute (e.g. the * "foo-bar" attribute would match the "fooBar" property). * * @see Conventions#attributeNameToPropertyName(String) * * @param builder the bean definition builder to be configured * @param element - the XML element where the attribute should be defined * @param attributeName - the name of the attribute whose value will be set on the property */ public static void setValueIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName) { setValueIfAttributeDefined(builder, element, attributeName, false); } /** * Configures the provided bean definition builder with a property value corresponding to the attribute whose name * is provided if that attribute is defined in the given element. * * @param builder the bean definition builder to be configured * @param element the XML element where the attribute should be defined * @param attributeName the name of the attribute whose value will be used to populate the property * @param propertyName the name of the property to be populated * @param emptyStringAllowed - if true, the value is set, even if an empty String (""); if false, an empty * String is treated as if the attribute wasn't provided. */ public static void setValueIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName, String propertyName, boolean emptyStringAllowed) { String attributeValue = element.getAttribute(attributeName); if (StringUtils.hasText(attributeValue) || (emptyStringAllowed && element.hasAttribute(attributeName))) { builder.addPropertyValue(propertyName, new TypedStringValue(attributeValue)); } } /** * Configures the provided bean definition builder with a property value corresponding to the attribute whose name * is provided if that attribute is defined in the given element. * * <p> * The property name will be the camel-case equivalent of the lower case hyphen separated attribute (e.g. the * "foo-bar" attribute would match the "fooBar" property). * * @see Conventions#attributeNameToPropertyName(String) * * @param builder the bean definition builder to be configured * @param element - the XML element where the attribute should be defined * @param attributeName - the name of the attribute whose value will be set on the property * @param emptyStringAllowed - if true, the value is set, even if an empty String (""); if false, an empty * String is treated as if the attribute wasn't provided. * */ public static void setValueIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName, boolean emptyStringAllowed) { setValueIfAttributeDefined(builder, element, attributeName, Conventions.attributeNameToPropertyName(attributeName), emptyStringAllowed); } /** * Configures the provided bean definition builder with a property reference to a bean. The bean reference is * identified by the value from the attribute whose name is provided if that attribute is defined in the given * element. * * @param builder the bean definition builder to be configured * @param element the XML element where the attribute should be defined * @param attributeName the name of the attribute whose value will be used as a bean reference to populate the * property * @param propertyName the name of the property to be populated */ public static void setReferenceIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName, String propertyName) { setReferenceIfAttributeDefined(builder, element, attributeName, propertyName, false); } public static void setReferenceIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName, String propertyName, boolean emptyStringAllowed) { if (element.hasAttribute(attributeName)) { String attributeValue = element.getAttribute(attributeName); if (StringUtils.hasText(attributeValue)) { builder.addPropertyReference(propertyName, attributeValue); } else if (emptyStringAllowed) { builder.addPropertyValue(propertyName, null); } } } /** * Configures the provided bean definition builder with a property reference to a bean. The bean reference is * identified by the value from the attribute whose name is provided if that attribute is defined in the given * element. * * <p> * The property name will be the camel-case equivalent of the lower case hyphen separated attribute (e.g. the * "foo-bar" attribute would match the "fooBar" property). * * @see Conventions#attributeNameToPropertyName(String) * * @param builder the bean definition builder to be configured * @param element - the XML element where the attribute should be defined * @param attributeName - the name of the attribute whose value will be used as a bean reference to populate the * property * * @see Conventions#attributeNameToPropertyName(String) */ public static void setReferenceIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName) { setReferenceIfAttributeDefined(builder, element, attributeName, false); } public static void setReferenceIfAttributeDefined(BeanDefinitionBuilder builder, Element element, String attributeName, boolean emptyStringAllowed) { setReferenceIfAttributeDefined(builder, element, attributeName, Conventions.attributeNameToPropertyName(attributeName), emptyStringAllowed); } /** * Provides a user friendly description of an element based on its node name and, if available, its "id" attribute * value. This is useful for creating error messages from within bean definition parsers. * * @param element The element. * @return The description. */ public static String createElementDescription(Element element) { String elementId = "'" + element.getNodeName() + "'"; String id = element.getAttribute("id"); if (StringUtils.hasText(id)) { elementId += " with id='" + id + "'"; } return elementId; } /** * Parse a "poller" element to provide a reference for the target BeanDefinitionBuilder. If the poller element does * not contain a "ref" attribute, this will create and register a PollerMetadata instance and then add it as a * property reference of the target builder. * * @param pollerElement the "poller" element to parse * @param targetBuilder the builder that expects the "trigger" property * @param parserContext the parserContext for the target builder */ public static void configurePollerMetadata(Element pollerElement, BeanDefinitionBuilder targetBuilder, ParserContext parserContext) { if (pollerElement.hasAttribute("ref")) { int numberOfAttributes = pollerElement.getAttributes().getLength(); if (numberOfAttributes != 1) { /* * When importing the core namespace, e.g. into jdbc, we get a 'default="false"' attribute, * even if not explicitly declared. */ if (!(numberOfAttributes == 2 && pollerElement.hasAttribute("default") && pollerElement.getAttribute("default").equals("false"))) { parserContext.getReaderContext().error( "A 'poller' element that provides a 'ref' must have no other attributes.", pollerElement); } } if (pollerElement.getChildNodes().getLength() != 0) { parserContext.getReaderContext().error( "A 'poller' element that provides a 'ref' must have no child elements.", pollerElement); } targetBuilder.addPropertyReference("pollerMetadata", pollerElement.getAttribute("ref")); } else { BeanDefinition beanDefinition = parserContext.getDelegate().parseCustomElement(pollerElement, targetBuilder.getBeanDefinition()); if (beanDefinition == null) { parserContext.getReaderContext().error("BeanDefinition must not be null", pollerElement); } targetBuilder.addPropertyValue("pollerMetadata", beanDefinition); } } /** * Get a text value from a named attribute if it exists, otherwise check for a nested element of the same name. * If both are specified it is an error, but if neither is specified, just returns null. * * @param element a DOM node * @param name the name of the property (attribute or child element) * @param parserContext the current context * @return the text from the attribute or element or null */ public static String getTextFromAttributeOrNestedElement(Element element, String name, ParserContext parserContext) { String attr = element.getAttribute(name); Element childElement = DomUtils.getChildElementByTagName(element, name); if (StringUtils.hasText(attr) && childElement != null) { parserContext.getReaderContext().error( "Either an attribute or a child element can be specified for " + name + " but not both", element); return null; } if (!StringUtils.hasText(attr) && childElement == null) { return null; } return StringUtils.hasText(attr) ? attr : childElement.getTextContent(); } public static BeanComponentDefinition parseInnerHandlerDefinition(Element element, ParserContext parserContext) { // parses out the inner bean definition for concrete implementation if defined List<Element> childElements = DomUtils.getChildElementsByTagName(element, "bean"); BeanComponentDefinition innerComponentDefinition = null; if (childElements != null && childElements.size() == 1) { Element beanElement = childElements.get(0); BeanDefinitionParserDelegate delegate = parserContext.getDelegate(); BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(beanElement); bdHolder = delegate.decorateBeanDefinitionIfRequired(beanElement, bdHolder); BeanDefinition inDef = bdHolder.getBeanDefinition(); innerComponentDefinition = new BeanComponentDefinition(inDef, bdHolder.getBeanName()); } String ref = element.getAttribute(REF_ATTRIBUTE); if (StringUtils.hasText(ref) && innerComponentDefinition != null) { parserContext.getReaderContext().error( "Ambiguous definition. Inner bean " + (innerComponentDefinition.getBeanDefinition().getBeanClassName()) + " declaration and \"ref\" " + ref + " are not allowed together on element " + IntegrationNamespaceUtils.createElementDescription(element) + ".", parserContext.extractSource(element)); } return innerComponentDefinition; } /** * Utility method to configure a HeaderMapper for Inbound and Outbound channel adapters/gateway. * * @param element The element. * @param rootBuilder The root builder. * @param parserContext The parser context. * @param headerMapperClass The header mapper class. * @param replyHeaderValue The reply header value. */ public static void configureHeaderMapper(Element element, BeanDefinitionBuilder rootBuilder, ParserContext parserContext, Class<?> headerMapperClass, String replyHeaderValue) { configureHeaderMapper(element, rootBuilder, parserContext, BeanDefinitionBuilder.genericBeanDefinition(headerMapperClass), replyHeaderValue); } /** * Utility method to configure a HeaderMapper for Inbound and Outbound channel adapters/gateway. * * @param element The element. * @param rootBuilder The root builder. * @param parserContext The parser context. * @param headerMapperBuilder The header mapper builder. * @param replyHeaderValue The reply header value. */ public static void configureHeaderMapper(Element element, BeanDefinitionBuilder rootBuilder, ParserContext parserContext, BeanDefinitionBuilder headerMapperBuilder, String replyHeaderValue) { String defaultMappedReplyHeadersAttributeName = "mapped-reply-headers"; if (!StringUtils.hasText(replyHeaderValue)) { replyHeaderValue = defaultMappedReplyHeadersAttributeName; } boolean hasHeaderMapper = element.hasAttribute("header-mapper"); boolean hasMappedRequestHeaders = element.hasAttribute("mapped-request-headers"); boolean hasMappedReplyHeaders = element.hasAttribute(replyHeaderValue); if (hasHeaderMapper && (hasMappedRequestHeaders || hasMappedReplyHeaders)) { parserContext.getReaderContext().error("The 'header-mapper' attribute is mutually exclusive with" + " 'mapped-request-headers' or 'mapped-reply-headers'. " + "You can only use one or the others", element); } IntegrationNamespaceUtils.setReferenceIfAttributeDefined(rootBuilder, element, "header-mapper"); if (hasMappedRequestHeaders || hasMappedReplyHeaders) { if (hasMappedRequestHeaders) { headerMapperBuilder.addPropertyValue("requestHeaderNames", element.getAttribute("mapped-request-headers")); } if (hasMappedReplyHeaders) { headerMapperBuilder.addPropertyValue("replyHeaderNames", element.getAttribute(replyHeaderValue)); } rootBuilder.addPropertyValue("headerMapper", headerMapperBuilder.getBeanDefinition()); } } /** * Parse a "transactional" element and configure a {@link TransactionInterceptor} * with "transactionManager" and other "transactionDefinition" properties. * For example, this advisor will be applied on the Polling Task proxy. * @param txElement The transactional element. * @return The bean definition. * @see AbstractPollingEndpoint */ public static BeanDefinition configureTransactionAttributes(Element txElement) { return configureTransactionAttributes(txElement, false); } /** * Parse a "transactional" element and configure a {@link TransactionInterceptor} * or {@link TransactionHandleMessageAdvice} * with "transactionManager" and other "transactionDefinition" properties. * For example, this advisor will be applied on the Polling Task proxy. * @param txElement The transactional element. * @param handleMessageAdvice flag if to use {@link TransactionHandleMessageAdvice} * or regular {@link TransactionInterceptor} * @return The bean definition. * @see AbstractPollingEndpoint */ public static BeanDefinition configureTransactionAttributes(Element txElement, boolean handleMessageAdvice) { BeanDefinition txDefinition = configureTransactionDefinition(txElement); BeanDefinitionBuilder attributeSourceBuilder = BeanDefinitionBuilder.genericBeanDefinition(MatchAlwaysTransactionAttributeSource.class); attributeSourceBuilder.addPropertyValue("transactionAttribute", txDefinition); BeanDefinitionBuilder txInterceptorBuilder = BeanDefinitionBuilder.genericBeanDefinition(handleMessageAdvice ? TransactionHandleMessageAdvice.class : TransactionInterceptor.class); txInterceptorBuilder.addPropertyReference("transactionManager", txElement.getAttribute("transaction-manager")); txInterceptorBuilder.addPropertyValue("transactionAttributeSource", attributeSourceBuilder.getBeanDefinition()); return txInterceptorBuilder.getBeanDefinition(); } /** * Parse attributes of "transactional" element and configure a {@link DefaultTransactionAttribute} * with provided "transactionDefinition" properties. * * @param txElement The transactional element. * @return The bean definition. */ public static BeanDefinition configureTransactionDefinition(Element txElement) { BeanDefinitionBuilder txDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(DefaultTransactionAttribute.class); txDefinitionBuilder.addPropertyValue("propagationBehaviorName", "PROPAGATION_" + txElement.getAttribute("propagation")); txDefinitionBuilder.addPropertyValue("isolationLevelName", "ISOLATION_" + txElement.getAttribute("isolation")); txDefinitionBuilder.addPropertyValue("timeout", txElement.getAttribute("timeout")); txDefinitionBuilder.addPropertyValue("readOnly", txElement.getAttribute("read-only")); return txDefinitionBuilder.getBeanDefinition(); } public static String[] generateAlias(Element element) { String[] handlerAlias = null; String id = element.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE); if (StringUtils.hasText(id)) { handlerAlias = new String[] { id + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX }; } return handlerAlias; } public static void configureAndSetAdviceChainIfPresent(Element adviceChainElement, Element txElement, BeanDefinition parentBeanDefinition, ParserContext parserContext) { configureAndSetAdviceChainIfPresent(adviceChainElement, txElement, false, parentBeanDefinition, parserContext); } public static void configureAndSetAdviceChainIfPresent(Element adviceChainElement, Element txElement, boolean handleMessageAdvice, BeanDefinition parentBeanDefinition, ParserContext parserContext) { configureAndSetAdviceChainIfPresent(adviceChainElement, txElement, handleMessageAdvice, parentBeanDefinition, parserContext, "adviceChain"); } public static void configureAndSetAdviceChainIfPresent(Element adviceChainElement, Element txElement, BeanDefinition parentBeanDefinition, ParserContext parserContext, String propertyName) { configureAndSetAdviceChainIfPresent(adviceChainElement, txElement, false, parentBeanDefinition, parserContext, propertyName); } @SuppressWarnings({ "rawtypes" }) public static void configureAndSetAdviceChainIfPresent(Element adviceChainElement, Element txElement, boolean handleMessageAdvice, BeanDefinition parentBeanDefinition, ParserContext parserContext, String propertyName) { ManagedList adviceChain = configureAdviceChain(adviceChainElement, txElement, handleMessageAdvice, parentBeanDefinition, parserContext); if (!CollectionUtils.isEmpty(adviceChain)) { parentBeanDefinition.getPropertyValues().add(propertyName, adviceChain); } } @SuppressWarnings("rawtypes") public static ManagedList configureAdviceChain(Element adviceChainElement, Element txElement, BeanDefinition parentBeanDefinition, ParserContext parserContext) { return configureAdviceChain(adviceChainElement, txElement, false, parentBeanDefinition, parserContext); } @SuppressWarnings({ "rawtypes", "unchecked" }) public static ManagedList configureAdviceChain(Element adviceChainElement, Element txElement, boolean handleMessageAdvice, BeanDefinition parentBeanDefinition, ParserContext parserContext) { ManagedList adviceChain = new ManagedList(); if (txElement != null) { adviceChain.add(configureTransactionAttributes(txElement, handleMessageAdvice)); } if (adviceChainElement != null) { NodeList childNodes = adviceChainElement.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node child = childNodes.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) { Element childElement = (Element) child; String localName = child.getLocalName(); if ("bean".equals(localName)) { BeanDefinitionHolder holder = parserContext.getDelegate().parseBeanDefinitionElement( childElement, parentBeanDefinition); parserContext.registerBeanComponent(new BeanComponentDefinition(holder)); adviceChain.add(new RuntimeBeanReference(holder.getBeanName())); } else if ("ref".equals(localName)) { String ref = childElement.getAttribute("bean"); adviceChain.add(new RuntimeBeanReference(ref)); } else { BeanDefinition customBeanDefinition = parserContext.getDelegate().parseCustomElement( childElement, parentBeanDefinition); if (customBeanDefinition == null) { parserContext.getReaderContext().error( "failed to parse custom element '" + localName + "'", childElement); } adviceChain.add(customBeanDefinition); } } } } return adviceChain; } public static BeanDefinition createExpressionDefinitionFromValueOrExpression(String valueElementName, String expressionElementName, ParserContext parserContext, Element element, boolean oneRequired) { Assert.hasText(valueElementName, "'valueElementName' must not be empty"); Assert.hasText(expressionElementName, "'expressionElementName' must not be empty"); String valueElementValue = element.getAttribute(valueElementName); String expressionElementValue = element.getAttribute(expressionElementName); boolean hasAttributeValue = StringUtils.hasText(valueElementValue); boolean hasAttributeExpression = StringUtils.hasText(expressionElementValue); if (hasAttributeValue && hasAttributeExpression) { parserContext.getReaderContext().error("Only one of '" + valueElementName + "' or '" + expressionElementName + "' is allowed", element); } if (oneRequired && (!hasAttributeValue && !hasAttributeExpression)) { parserContext.getReaderContext().error("One of '" + valueElementName + "' or '" + expressionElementName + "' is required", element); } BeanDefinition expressionDef; if (hasAttributeValue) { expressionDef = new RootBeanDefinition(LiteralExpression.class); expressionDef.getConstructorArgumentValues().addGenericArgumentValue(valueElementValue); } else { expressionDef = createExpressionDefIfAttributeDefined(expressionElementName, element); } return expressionDef; } public static BeanDefinition createExpressionDefIfAttributeDefined(String expressionElementName, Element element) { Assert.hasText(expressionElementName, "'expressionElementName' must no be empty"); String expressionElementValue = element.getAttribute(expressionElementName); if (StringUtils.hasText(expressionElementValue)) { BeanDefinitionBuilder expressionDefBuilder = BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class); expressionDefBuilder.addConstructorArgValue(expressionElementValue); return expressionDefBuilder.getRawBeanDefinition(); } return null; } public static String createDirectChannel(Element element, ParserContext parserContext) { String channelId = element.getAttribute(AbstractBeanDefinitionParser.ID_ATTRIBUTE); if (!StringUtils.hasText(channelId)) { parserContext.getReaderContext().error("The channel-adapter's 'id' attribute is required when no 'channel' " + "reference has been provided, because that 'id' would be used for the created channel.", element); } IntegrationConfigUtils.autoCreateDirectChannel(channelId, parserContext.getRegistry()); return channelId; } @SuppressWarnings("unchecked") public static void checkAndConfigureFixedSubscriberChannel(Element element, ParserContext parserContext, String channelName, String handlerBeanName) { BeanDefinitionRegistry registry = parserContext.getRegistry(); if (registry.containsBeanDefinition(channelName)) { BeanDefinition inputChannelDefinition = registry.getBeanDefinition(channelName); if (FixedSubscriberChannel.class.getName().equals(inputChannelDefinition.getBeanClassName())) { ConstructorArgumentValues constructorArgumentValues = inputChannelDefinition .getConstructorArgumentValues(); if (constructorArgumentValues.isEmpty()) { constructorArgumentValues.addGenericArgumentValue(new RuntimeBeanReference(handlerBeanName)); } else { parserContext.getReaderContext() .error("Only one subscriber is allowed for a FixedSubscriberChannel.", element); } } } else { BeanDefinition bfppd; if (!registry.containsBeanDefinition(IntegrationContextUtils.INTEGRATION_FIXED_SUBSCRIBER_CHANNEL_BPP_BEAN_NAME)) { bfppd = new RootBeanDefinition(FixedSubscriberChannelBeanFactoryPostProcessor.class); registry.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_FIXED_SUBSCRIBER_CHANNEL_BPP_BEAN_NAME, bfppd); } else { bfppd = registry.getBeanDefinition(IntegrationContextUtils.INTEGRATION_FIXED_SUBSCRIBER_CHANNEL_BPP_BEAN_NAME); } ManagedMap<String, String> candidates; ValueHolder argumentValue = bfppd.getConstructorArgumentValues().getArgumentValue(0, Map.class); if (argumentValue == null) { candidates = new ManagedMap<String, String>(); bfppd.getConstructorArgumentValues().addIndexedArgumentValue(0, candidates); } else { candidates = (ManagedMap<String, String>) argumentValue.getValue(); } candidates.put(handlerBeanName, channelName); } } public static void putLifecycleInRole(String role, String beanName, ParserContext parserContext) { BeanDefinitionRegistry registry = parserContext.getRegistry(); IntegrationConfigUtils.registerRoleControllerDefinitionIfNecessary(registry); BeanDefinition controllerDef = registry.getBeanDefinition( IntegrationContextUtils.INTEGRATION_LIFECYCLE_ROLE_CONTROLLER); @SuppressWarnings("unchecked") ManagedList<String> roles = (ManagedList<String>) controllerDef.getConstructorArgumentValues() .getArgumentValue(0, ManagedList.class).getValue(); @SuppressWarnings("unchecked") ManagedList<BeanReference> lifecycles = (ManagedList<BeanReference>) controllerDef.getConstructorArgumentValues() .getArgumentValue(1, ManagedList.class).getValue(); roles.add(role); lifecycles.add(new RuntimeBeanReference(beanName)); } public static void injectPropertyWithAdapter(String beanRefAttribute, String methodRefAttribute, String expressionAttribute, String beanProperty, String adapterClass, Element element, BeanDefinitionBuilder builder, BeanMetadataElement processor, ParserContext parserContext) { BeanMetadataElement adapter = constructAdapter(beanRefAttribute, methodRefAttribute, expressionAttribute, adapterClass, element, processor, parserContext); builder.addPropertyValue(beanProperty, adapter); } public static void injectConstructorWithAdapter(String beanRefAttribute, String methodRefAttribute, String expressionAttribute, String adapterClass, Element element, BeanDefinitionBuilder builder, BeanMetadataElement processor, ParserContext parserContext) { BeanMetadataElement adapter = constructAdapter(beanRefAttribute, methodRefAttribute, expressionAttribute, adapterClass, element, processor, parserContext); builder.addConstructorArgValue(adapter); } private static BeanMetadataElement constructAdapter(String beanRefAttribute, String methodRefAttribute, String expressionAttribute, String adapterClass, Element element, BeanMetadataElement processor, ParserContext parserContext) { final String beanRef = element.getAttribute(beanRefAttribute); final String beanMethod = element.getAttribute(methodRefAttribute); final String expression = element.getAttribute(expressionAttribute); final boolean hasBeanRef = StringUtils.hasText(beanRef); final boolean hasExpression = StringUtils.hasText(expression); if (hasBeanRef && hasExpression) { parserContext.getReaderContext().error("Exactly one of the '" + beanRefAttribute + "' or '" + expressionAttribute + "' attribute is allowed.", element); } BeanMetadataElement adapter = null; if (hasBeanRef) { adapter = createAdapter(new RuntimeBeanReference(beanRef), beanMethod, adapterClass); } else if (hasExpression) { BeanDefinitionBuilder adapterBuilder = BeanDefinitionBuilder .genericBeanDefinition(IntegrationConfigUtils.BASE_PACKAGE + ".aggregator.ExpressionEvaluating" + adapterClass); adapterBuilder.addConstructorArgValue(expression); adapter = adapterBuilder.getBeanDefinition(); } else if (processor != null) { adapter = createAdapter(processor, beanMethod, adapterClass); } else { adapter = createAdapter(null, beanMethod, adapterClass); } return adapter; } private static BeanMetadataElement createAdapter(BeanMetadataElement ref, String method, String unqualifiedClassName) { BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition(IntegrationConfigUtils.BASE_PACKAGE + ".config." + unqualifiedClassName + "FactoryBean"); builder.addPropertyValue("target", ref); if (StringUtils.hasText(method)) { builder.addPropertyValue("methodName", method); } return builder.getBeanDefinition(); } }